﻿using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace LightCAD.Core
{
    public enum LcDataType
    {
        String,
        Float,
        Double,
        Int,
        Long,
        Bool,
        DateTime,
        Point2d,
        Point3d,
        CurveArray,
        Curve2d
    }
    public class ParameterDefinition
    {
        public string Name { get; set; }
        public string DisplayName { get; set; }
        public string Description { get; set; }
        public bool IsArray { get; set; }
        public LcDataType DataType { get; set; }
        public int Index { get; set; }

        public object DefaultValue { get; set; }

        public void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            writer.WriteStartObject();
            writer.WriteStringProperty(nameof(this.Name), this.Name);
            writer.WriteStringProperty(nameof(this.DisplayName), this.DisplayName);
            writer.WriteStringProperty(nameof(this.Description), this.Description);
            writer.WriteBoolProperty(nameof(this.IsArray), this.IsArray);
            writer.WriteNumberProperty(nameof(this.DataType), (int)DataType);
            writer.WriteNumberProperty(nameof(this.Index), this.Index);
            writer.WriteObjectProperty(nameof(this.DefaultValue), this.DefaultValue, soptions);
            writer.WriteEndObject();
        }

        public void ReadProperties(ref JsonElement jele)
        {
            this.Name = jele.ReadStringProperty(nameof(this.Name));
            this.DisplayName = jele.ReadStringProperty(nameof(this.DisplayName));
            this.Description = jele.ReadStringProperty(nameof(this.Description));
            this.IsArray = (bool)jele.ReadBoolProperty(nameof(this.IsArray));
            this.DataType = (LcDataType)jele.ReadIntProperty(nameof(this.DataType));
            this.Index = jele.ReadIntProperty(nameof(this.Index));
            this.DefaultValue = jele.ReadObjectProperty<object> (nameof(this.DefaultValue));
        }
    }

    public class ParameterSetDefinition : KeyedCollection<string, ParameterDefinition>
    {
        protected override string GetKeyForItem(ParameterDefinition item)
        {
            return item.Name;
        }
        protected override void InsertItem(int index, ParameterDefinition item)
        {
            base.InsertItem(index, item);
            item.Index = index;
        }
        protected override void SetItem(int index, ParameterDefinition item)
        {
            base.SetItem(index, item);
            item.Index = index;
        }

        public LcParameterSet New()
        {
            return new LcParameterSet(this);
        }

        public void WriterProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            writer.WriteStartArray();
            foreach (var item in this)
            {
                item.WriteProperties(writer, soptions);
            }
            writer.WriteEndArray();
        }

        public void ReadProperties(ref JsonElement jele)
        {
            var arr = jele.EnumerateArray().ToArray();
            for (var i = 0; i < arr.Length; i++)
            {
                var item = arr[i];
                var paramDef = new ParameterDefinition();
                paramDef.ReadProperties(ref item);
                this.Insert(i, paramDef);
            }
        }
    }
    public class LcParameterSet
    {
        public ParameterSetDefinition Definition { get; set; }

        public object[] Values { get; set; }

        public object this[int index]
        {
            get { return Values[index]; }
            set { Values[index] = value; }
        }
        public object this[string name]
        {
            get
            {
                var def = Definition[name];
                var value = Values[def.Index];
                if (value == null)
                {
                    value = def.DefaultValue;
                }
                return value;
            }
            set
            {
                var def = Definition[name];
                this.Values[def.Index] = value;
            }
        }
        public LcParameterSet(ParameterSetDefinition definition)
        {
            this.Definition = definition;
            Values = new object[Definition.Count];
        }

        public T GetValue<T>(int index)
        {
            var value = Values[index];
            if (value == null)
            {
                value = Definition[index].DefaultValue;
            }
            return (T)value;
        }
        public T GetValue<T>(string name)
        {
            var def = Definition[name];
            var value = Values[def.Index];
            if (value == null)
            {
                value = def.DefaultValue;
            }
            return value == null ? default(T) : (T)value;
        }
        public void SetValue(int index, object value)
        {
            this.Values[index] = value;
        }
        public void SetValue(string name, object value)
        {
            var def = Definition[name];
            this.Values[def.Index] = value;
        }

        public LcParameterSet Clone()
        {
            var paramSet = new LcParameterSet(this.Definition);
            paramSet.Copy(this);
            return paramSet;
        }
        public void Copy(LcParameterSet set)
        {
            foreach (var def in set.Definition)
            {
                this.SetValue(def.Name, set[def.Name]);
            }
        }
        public void WriterProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            writer.WriteStartObject();
            //writer.WritePropertyName(nameof(this.Definition));
            //this.Definition.WriterProperties(writer, soptions);
            writer.WriteCollectionProperty<object>(nameof(this.Values), this.Values, soptions, (w, val) => JsonSerializer.Serialize(writer, val, soptions));
            writer.WriteEndObject();
        }

        public void ReadProperties(ref JsonElement jele)
        {
            //var paramDefSet = new ParameterSetDefinition();
            //paramDefSet.ReadProperties(ref jele);
            //this.Definition = paramDefSet;


            var exist = jele.TryGetProperty(nameof(this.Values), out JsonElement prop);
            if (exist && prop.NullOrUndefined())
            {
                var arr = jele.EnumerateArray().ToArray();
                for (var i = 0; i < arr.Length; i++)
                {
                    var item = arr[i];
                    var obj = JsonSerializer.Deserialize<object>(item);
                    this.SetValue(i, obj);
                }
            }
        }
    }
}